package net.bcxuexi.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import net.bcxuexi.config.Config;
import net.bcxuexi.exception.MyException;
import net.bcxuexi.ports.LoadPorts;
import net.bcxuexi.ports.model.IPort;
import net.bcxuexi.server.model.ServerSocketModel;
import net.bcxuexi.server.model.SocketModel;
import net.bcxuexi.server.stream.AcceptSocket;
import net.bcxuexi.server.stream.StreamData;
import net.bcxuexi.server.stream.outwrap.OutputDataWrap;
import net.bcxuexi.tools.IdGen;
import net.bcxuexi.tools.MyLog;
import net.bcxuexi.tools.StringUtils;

/**
 * 负责ServerSocket的创建及管理
 */
public class ServerSocketManager {
	private List<ServerSocketModel> serverSocketModels = new ArrayList<ServerSocketModel>();
	private static ServerSocketManager serverSocketManager;
	/**
	 * 已建立的iport
	 */
	private List<IPort> clientIPorts = new ArrayList<IPort>();

	private ServerSocketManager() {

	}

	/**
	 * 初始化serversocket服务
	 */
	private void init() {
		// 每10秒钟判断下iport是否有更改
		Timer timer = new Timer();
		timer.schedule(readLatestIPortTask, 1000, 10000);
		//每20秒向客户端发送待穿透端口
		Timer timer2 = new Timer();
		timer2.schedule(sendIPortsTask, 1000, 20000);
	}

	/**
	 * 读取配置信息，是否添加了新的iport或者删除了iport
	 */
	private TimerTask readLatestIPortTask = new TimerTask() {
		@Override
		public void run() {
			int serverPort = LoadPorts.getInstance().getServerPort();
			// 是否在serverPort端口启动了serversocket
			ServerSocketModel serverSocketModel = findServerSocketModelByPort(serverPort);
			if (serverSocketModel == null) {
				// 创建中心控制服务serversocket
				try {
					createServerSocket(serverPort,
							Config.ServerSocketType.server,null);
					System.out.println("控制服务启动成功!");
				} catch (MyException e) {
					System.out.println("控制服务启动失败!");
					e.printStackTrace();
				}
			}
			// 比较clientIPorts 和 cIPorts找出需要新增的iPort或者需要删除的iport
			List<IPort> cIPorts = LoadPorts.getInstance().getAll();
			// 删除iport
			List<IPort> delIPorts = new ArrayList<IPort>();
			for (Iterator iterator = clientIPorts.iterator(); iterator
					.hasNext();) {
				IPort iport = (IPort) iterator.next();
				boolean exist = false;
				for (Iterator iterator2 = cIPorts.iterator(); iterator2
						.hasNext();) {
					IPort newIport = (IPort) iterator2.next();
					if (iport.getIp().equals(newIport.getIp())
							&& iport.getPort() == newIport.getPort()
							&& iport.getProxy() == newIport.getProxy()) {
						exist = true;
						break;
					}
				}
				if (!exist) {
					delIPorts.add(iport);
				}
			}
			for (IPort iport : delIPorts) {
				clientIPorts.remove(iport);
				// 检查是否需要停止proxy serversocket
				ServerSocketModel iportSocketModel = findServerSocketModelByIPort(iport);
				iportSocketModel.removeIport(iport);
				if(iportSocketModel.getIports()==null
						||iportSocketModel.getIports().size()==0){
					serverSocketModels.remove(iportSocketModel);
					iportSocketModel.close("没有任何代理配置，删除代理服务"+iportSocketModel.toString());
				}
			}
			// 新增iport
			List<IPort> addIPorts = new ArrayList<IPort>();
			for (Iterator iterator = cIPorts.iterator(); iterator.hasNext();) {
				IPort newIport = (IPort) iterator.next();
				boolean has = false;
				for (Iterator iterator2 = clientIPorts.iterator(); iterator2
						.hasNext();) {
					IPort iport = (IPort) iterator2.next();
					if (iport.getIp().equals(newIport.getIp())
							&& iport.getPort() == newIport.getPort()
							&& iport.getProxy() == newIport.getProxy()) {
						has = true;
						break;
					}
				}
				if (!has) {
					// 添加
					clientIPorts.add(newIport);
					addIPorts.add(newIport);
				}
			}
			// 为新增的iport创建代理serversocket
			for (IPort iport : addIPorts) {
				int proxy = iport.getProxy();
				ServerSocketModel proxyServerSocketModel = findServerSocketModelByPort(proxy);
				if (proxyServerSocketModel != null) {
					continue;
				}
				try {
					createServerSocket(proxy, Config.ServerSocketType.proxy,iport);
					System.out.println("代理服务启动成功!" + iport.toString());
				} catch (MyException e) {
					System.out.println("代理服务启动失败!" + iport.toString());
					e.printStackTrace();
				}
			}
		}
	};
	/**
	 * 向客户端发送待穿透的端口
	 */
	private TimerTask sendIPortsTask = new TimerTask() {
		
		@Override
		public void run() {
			Map<String, List<IPort>> chientIPortsMap = new HashMap<String, List<IPort>>();
			for(IPort iport:clientIPorts){
				String clientKey = iport.getClientKey();
				List<IPort> cIPorts = chientIPortsMap.get(clientKey);
				if(cIPorts==null){
					cIPorts = new ArrayList<IPort>();
					chientIPortsMap.put(clientKey, cIPorts);
				}
				cIPorts.add(iport);
			}
			//发送端口
			Iterator iter = chientIPortsMap.entrySet().iterator();
			while (iter.hasNext()) {
				Map.Entry entry = (Map.Entry) iter.next();
				String clientKey = (String) entry.getKey();
				List<IPort> cIPorts = (List<IPort>) entry.getValue();
				//查找clientKey的控制socket
				SocketModel controlSocketModel = SocketManager.getInstance().findControlSocketModelByClientKey(clientKey);
				if(controlSocketModel==null||!controlSocketModel.isActive()){
					MyLog.info("找不到控制通信socket,无法发送端口.clientkey="+clientKey);
					continue;
				}
				// 发送内网穿透端口
				OutputDataWrap outDataWrap = new OutputDataWrap();
				StreamData outputStreamData = outDataWrap
						.scPorts(controlSocketModel,cIPorts);
				MyLog.info("发送端口");
				try {
					outputStreamData.getSocketModel().getWriteStream()
							.addData(outputStreamData);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	};

	/**
	 * 创建一个serversocket,控制类型的serversocket只允许创建一个
	 * 
	 * @param port
	 *            serversocket端口
	 * @param type
	 *            serversocket类型
	 * @throws MyException
	 */
	public ServerSocketModel createServerSocket(int port, String type,IPort iport)
			throws MyException {

		try {
			ServerSocket serverSocket = new ServerSocket(port);
			ServerSocketModel serverSocketModel = new ServerSocketModel();
			serverSocketModel.setId(IdGen.uuid());
			serverSocketModel.setPort(port);
			serverSocketModel.setServerSocket(serverSocket);
			serverSocketModel.setType(type);
			if(iport!=null){
				serverSocketModel.addIport(iport);
			}
			// 监听客户端连接
			AcceptSocket acceptSocket = new AcceptSocket(serverSocketModel);
			serverSocketModel.setAcceptSocket(acceptSocket);
			acceptSocket.start();
			serverSocketModels.add(serverSocketModel);
			return serverSocketModel;
		} catch (IOException e) {
			throw new MyException("", e.getMessage()+"端口"+port, e);
		}
	}

	/**
	 * 通过端口号查询serverSocket
	 */
	public ServerSocketModel findServerSocketModelByPort(int port) {
		for (ServerSocketModel serverSocketModel : serverSocketModels) {
			if (port == serverSocketModel.getPort()) {
				return serverSocketModel;
			}
		}
		return null;
	}

	/**
	 * 通过id查询serverSocket
	 */
	public ServerSocketModel findServerSocketModelById(String id) {
		if (StringUtils.isBlack(id)) {
			return null;
		}
		for (ServerSocketModel serverSocketModel : serverSocketModels) {
			if (id.equals(serverSocketModel.getId())) {
				return serverSocketModel;
			}
		}
		return null;
	}
	/**
	 * 通过id查询serverSocket
	 */
	public ServerSocketModel findServerSocketModelByIPort(IPort iport) {
		if (iport==null) {
			return null;
		}
		for (ServerSocketModel serverSocketModel : serverSocketModels) {
			List<IPort> iports = serverSocketModel.getIports();
			for(IPort port:iports){
				if(iport.getIp().equals(port.getIp())&&iport.getPort()==port.getPort()
						&&iport.getProxy()==port.getProxy()&&iport.getClientKey().equals(port.getClientKey())){
					return serverSocketModel;
				}
			}
		}
		return null;
	}

	/**
	 * 通过代理端口查找iport,如果有多个取第一个.
	 * 注意：此方法有bug，无法区分多个客户端,并且一个端口无法映射到两个内网ip
	 * @param proxy
	 * @return
	 */
	public IPort findIportByProxy(int proxy){
		for (Iterator iterator = clientIPorts.iterator(); iterator.hasNext();) {
			IPort iport = (IPort) iterator.next();
			if(iport.getProxy()==proxy){
				return iport;
			}
		}
		return null;
	}

	

	public static synchronized ServerSocketManager getInstance() {
		if (serverSocketManager == null) {
			serverSocketManager = new ServerSocketManager();
			serverSocketManager.init();
		}
		return serverSocketManager;
	}
}
